package com.heima.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.heima.annotation.Speed;
import com.heima.dto.DishDto;
import com.heima.dto.FlavorDto;
import com.heima.mapper.DishMapper;
import com.heima.pojo.Dish;
import com.heima.pojo.DishFlavor;
import com.heima.service.DishService;
import com.heima.vo.DishVo;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author itheima
 * @since 2022-11-25
 */
@Slf4j
@Service
public class DishServiceImpl implements DishService {

    @Autowired
    private DishMapper dishMapper;

    @Transactional(rollbackFor = RuntimeException.class)
    @Override
    public Boolean addDish(DishDto dishDto) {

        // 把前端传过来的参数，对应写到它的表中
        // 菜品信息 -> 菜品表
        // 1. 把dishDto中属于菜品的东西提取出来，放到Dish实体类里面
        Dish dish = new Dish();
        // 拷贝同名和同类型的字段值
        BeanUtils.copyProperties(dishDto, dish);

        // 2. 调用dish表insert方法，写入这部分信息
        int result = dishMapper.insert(dish);

        // 口味信息 -> 口味表
        // 1. 获取当前菜品的口味集合
        List<FlavorDto> flavors = dishDto.getFlavors();

        // 如果用循环+插入的方式，当我调用一次接口后，就会重复执行多次sql操作
        // 导致性能下降
        List<DishFlavor> newList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(flavors)) {
            // 2. 遍历集合并且插入到数据库中
            for (FlavorDto item : flavors) {
                if (item == null) {
                    continue;
                }

                // 2.1 将接收到的参数 转成Flavor结构
                DishFlavor flavor = new DishFlavor();

                BeanUtils.copyProperties(item, flavor);

                newList.add(flavor);

                // 2.2 执行insert插入到数据库中
//                int rs = dishMapper.insertFlavor(flavor);
//                if (rs != 1) {
//                    throw new RuntimeException("口味添加失败");
//                }
            }
        }

        int rs = dishMapper.batchInsertFlavor(newList);
        if (rs != newList.size()) {
            throw new RuntimeException("口味添加失败");
        }

        return result == 1;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean modifyDish(DishDto dishDto) {
        Integer a = 1;

        if (dishDto == null) {
            throw new RuntimeException("参数不能为空");
        }

        // 1. 提取菜品信息，覆盖到菜品里面
        // 1.1 创建一个dish对象
        Dish dish = new Dish();
        // 1.2 从dishDto中提取菜品相关数据，封装到dish对象中
        BeanUtils.copyProperties(dishDto, dish);
        // 1.3 调用mapper，update菜品信息
        int dishUpdateResult = dishMapper.updateById(dish);
        if (dishUpdateResult != 1) {
            // 流程执行失败，导致流程退出
            log.error("菜品添加失败");
            return false;
        }

        // 2. 更新菜品与口味的关系
        int total = dishMapper.countFlavorByDishId(dishDto.getId());
        if (total > 0) {
            // 2.1 根据菜品id删除所有关联的口味信息
            int removeResult = dishMapper.removeFlavorByDishId(dishDto.getId());
            if (removeResult != total) {
                throw new RuntimeException("口味删除失败");
            }
        }

        List<FlavorDto> flavors = dishDto.getFlavors();

        // 2.2 将dishDto中的口味信息添加到口味表中
        List<DishFlavor> newList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(flavors)) {
            for (FlavorDto item : flavors) {
                if (item == null) {
                    continue;
                }

                // 2.1 将接收到的参数 转成Flavor结构
                DishFlavor flavor = new DishFlavor();
                BeanUtils.copyProperties(item, flavor);
                flavor.setDishId(dishDto.getId());
                newList.add(flavor);
            }
        }

        int rs = dishMapper.batchInsertFlavor(newList);
        if (rs != newList.size()) {
            throw new RuntimeException("口味添加失败");
        }

        return true;
    }

    @Override
    public Dish getById(String dishId) {
        return dishMapper.selectById(dishId);
    }

    @Override
    public List<DishVo> getDishList(String categoryId, Integer status, Integer page, Integer pageSize) {
        // 1. 入参判断
        if (StringUtils.isEmpty(categoryId) || Objects.isNull(status)) {
            return new ArrayList<>();
        }

        // 2. 查询菜品表的集合
        // SQL里面分页关键字 limit 从第几条开始,总共查几条
        // limit 20, 10
        // 1,10  -> 0,10
        // 2,10  -> 10,10
        // 3,10  -> 20,10
        // (page - 1) * pageSize
        page = (page - 1) * pageSize;
        List<Dish> dishList = dishMapper.selectByCateIdAndStatus(categoryId, status, page, pageSize);
        if (CollectionUtils.isEmpty(dishList)) {
            return new ArrayList<>();
        }

        // 3. 提取菜品主键集合
        // filter(Objects::nonNull) 将集合中，null的数据先过滤掉，避免后续处理的时候空指针
        // map(Dish::getId) 每次遍历，将遍历到的对象中的id字段，提取出来
        // collect 将前面获得的数据，收集起来，转成集合，并返回
        List<String> dishIdList = dishList.stream().filter(Objects::nonNull).map(Dish::getId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(dishIdList)) {
            return new ArrayList<>();
        }

        List<DishVo> voList = new ArrayList<>();

        // 4. 根据菜品主键集合 查询口味的集合
        List<DishFlavor> flavorList = dishMapper.selectFlavorByDishIds(dishIdList);
        if (!CollectionUtils.isEmpty(flavorList)) {
            // 5. 将菜品集合和口味集合 一一对应进行绑定
            // 5.1 遍历一下菜品集合
            // 5.1.1 内部嵌套口味的遍历

            for (Dish dish : dishList) {
                if (Objects.isNull(dish)) {
                    continue;
                }

                // 获取当前菜品的主键
                String dishId = dish.getId();
                String dishName = dish.getName();

                // 把查询出来的菜品，赋值给DishVo
                DishVo vo = new DishVo();
                BeanUtils.copyProperties(dish, vo);

                System.out.println("菜品名：" + dishName + ",编号：" + dishId);

                // 第三步：拿着当前遍历出来的菜品id
                // 到口味集合中，去查找哪些口味跟我当前菜品有关系
                // 最终把这些有关系的口味，组装成一个集合，并且set到Vo里面
                List<DishFlavor> currentFlavors = new ArrayList<>();
                for (DishFlavor dishFlavor : flavorList) {
                    if (Objects.isNull(dishFlavor)) {
                        continue;
                    }

                    String currentDishId = dishFlavor.getDishId();
                    if (dishId.equals(currentDishId)) {
                        // 当前的口味，属于 xxx 菜品
                        currentFlavors.add(dishFlavor);
                    }
                }

                vo.setFlavors(currentFlavors);

                voList.add(vo);
            }
        }


        // 6. 返回结果
        return voList;
    }

    @Override
    public List<DishVo> getDishListSimple(String categoryId, Integer status, Integer page, Integer pageSize) {

        // 1. 入参判断
        if (StringUtils.isEmpty(categoryId) || Objects.isNull(status)) {
            return new ArrayList<>();
        }

        // 2. 查询菜品表的集合
        List<Dish> dishList = dishMapper.selectByCateIdAndStatus(categoryId, status, page, pageSize);
        if (CollectionUtils.isEmpty(dishList)) {
            return new ArrayList<>();
        }

        // 性能差：
        List<DishVo> voList = new ArrayList<>();
        // 3. 遍历菜品（执行7次SQL）
        for (Dish dish : dishList) {
            if (Objects.isNull(dish)) {
                continue;
            }

            String dishId = dish.getId();

            DishVo vo = new DishVo();
            BeanUtils.copyProperties(dish, vo);

            // 得到当前菜品的专属口味
            // 4. 每次遍历后，查询当前菜品口味信息
            List<DishFlavor> dishFlavor = dishMapper.selectFlavorByDishId(dishId);
            vo.setFlavors(dishFlavor);
        }

        // 5. 将当前查出的菜品和口味进行组装返回
        return voList;
    }

    @Override
    @Speed
    public List<DishVo> getDishListPlus(String categoryId, Integer status, Integer page, Integer pageSize) {
        // 1. 入参判断
        if (StringUtils.isEmpty(categoryId) || Objects.isNull(status)) {
            return new ArrayList<>();
        }

        // 2. 查询菜品表的集合
        List<Dish> dishList = dishMapper.selectByCateIdAndStatus(categoryId, status, page, pageSize);
        if (CollectionUtils.isEmpty(dishList)) {
            return new ArrayList<>();
        }

        // 3. 提取菜品主键集合
        // filter(Objects::nonNull) 将集合中，null的数据先过滤掉，避免后续处理的时候空指针
        // map(Dish::getId) 每次遍历，将遍历到的对象中的id字段，提取出来
        // collect 将前面获得的数据，收集起来，转成集合，并返回
        List<String> dishIdList = dishList.stream().filter(Objects::nonNull).map(Dish::getId).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(dishIdList)) {
            return new ArrayList<>();
        }

        List<DishVo> voList = new ArrayList<>();

        // 4. 根据菜品主键集合 查询口味的集合
        List<DishFlavor> flavorList = dishMapper.selectFlavorByDishIds(dishIdList);

        // 5. 集合转Map
        // key：每个口味 dishId （key（口味对应的菜品id） => ArrayList(两条口味记录))
        Map<String, ArrayList<DishFlavor>> flavorMap = new HashMap<>();
        for (DishFlavor dishFlavor : flavorList) {
            // 当前这条口味的菜品id => 666
            String dishId = dishFlavor.getDishId();

            // 通过菜品id，到HashMap中查询是否有值
            ArrayList<DishFlavor> dishFlavors = flavorMap.get(dishId);

            // 两种情况：
            // 存在
            // 不存在
            if (CollectionUtil.isEmpty(dishFlavors)) {
                dishFlavors = new ArrayList<>();
            }

            dishFlavors.add(dishFlavor);

            // key => 1234
            // 值 => ArrayList() => 会有两条记录
            // key => 666
            // 值 => ArrayList() => 会有一条记录
            flavorMap.put(dishId, dishFlavors);
        }

        // 6. 组装数据 菜品100条，口味100条 100x100 = 10000
        for (Dish dish : dishList) {
            if (Objects.isNull(dish)) {
                continue;
            }

            DishVo vo = new DishVo();
            BeanUtils.copyProperties(dish, vo);
            vo.setFlavors(flavorMap.get(dish.getId()));
            voList.add(vo);
        }

        // 7. 返回结果
        return voList;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Boolean removeDish(String ids) {

        // id作为条件，批量删除dish表和flavor表的记录

        // 删除dish
        // 掉什么方法、传什么参数？
        // 字符串参数 -> 集合类型的参数
        if (StringUtils.isEmpty(ids)) {
            return true;
        }

        String[] split = ids.split(",");
        if (split.length == 0) {
            throw new RuntimeException("入参不能为空");
        }

        List<String> list = Arrays.asList(split);
        if (CollectionUtil.isEmpty(list)) {
            throw new RuntimeException("入参不能为空");
        }

        Boolean removeResult = dishMapper.removeDishByIds(list);
        if (!removeResult) {
            throw new RuntimeException("删除菜品异常");
        }

        // 删除dish_flavors
        Boolean flavorRemoveResult = dishMapper.removeFlavorByDishIds(list);
        if(!flavorRemoveResult) {
            throw new RuntimeException("删除口味异常");
        }

        return true;
    }
}
